#include "Sphere.h"

point4 CSphere::m_tetrahedron_points[4] = { point4( 0.0f, 0.0f, 1.0f, 1.0f ),
											point4( 0.0f, 0.942809f, -0.333333f, 1.0f ),
											point4( -0.816497f, -0.471405f, -0.333333f, 1.0f ),
											point4( 0.816497f, -0.471405f, -0.333333f, 1.0f ) };

CSphere::CSphere( vec3 location, float size, float rotationSpeed, SHADING shading, COMPLEXITY complexity)
  : m_initloc(location),
	m_size(size),
	m_speed(rotationSpeed),
	m_shading(shading),
	m_host(NULL),
	m_distanceFromHost(0),
	m_rotated(0),
	m_index(0),
	m_points(NULL),
	m_normals(NULL),
	m_pointsSize(0),
	m_normalsSize(0)
{
	initSphere(complexity);
}

CSphere::CSphere(CSphere* host, float distFromHost, float size, float rotationSpeed, SHADING shading, COMPLEXITY complexity)
  : m_size(size),
	m_speed(rotationSpeed),
	m_shading(shading),
	m_host(host),
	m_distanceFromHost(distFromHost),
	m_rotated(0),
	m_index(0),
	m_points(NULL),
	m_normals(NULL),
	m_pointsSize(0),
	m_normalsSize(0)
{
	m_initloc = vec3(distFromHost, 1, 1);

	initSphere(complexity);
}

CSphere::~CSphere(void)
{
}

void CSphere::initSphere(COMPLEXITY complexity)
{
	int NumTimesToSubdivide;
	int NumTriangles;

	switch (complexity)
	{
	case MED_LOW:
		NumTimesToSubdivide = 4;
		NumTriangles = 1024;
		break;
	case MED_HIGH:
		NumTimesToSubdivide = 5;
		NumTriangles = 4096;
		break;
	case HIGH:
		NumTimesToSubdivide = 5;
		NumTriangles = 16384;
		break;
	}

	m_numVertices = NumTriangles * 3;

	m_points = new point4[m_numVertices];
	m_pointsSize = m_numVertices * sizeof(point4);
	
	m_normals = new point3[m_numVertices];
	m_normalsSize = m_numVertices * sizeof(point3);

	// subdivide a tetrahedron into a sphere
    tetrahedron( NumTimesToSubdivide );
}

mat4 CSphere::GetTranslationMatrix()
{
	if (m_host)
	{
		return RotateY(m_host->m_rotated) * Translate(m_host->m_initloc.x/2, 0, 0) * GetRotationMatrix();// * Translate(-1 * m_host->m_initloc) * RotateY(m_host->m_rotated);
	}
	else
	{ 
		return GetRotationMatrix();
	}
}

mat4 CSphere::GetRotationMatrix()
{
	m_rotated += 0.025f * m_speed;

	if (m_rotated > 360.0)
	{
		m_rotated -= 360.0;
	}
	
	return RotateY(m_rotated);
}

void CSphere::triangle( const point4& a, const point4& b, const point4& c )
{
	m_points[m_index] = (Scale(m_size, m_size, m_size) * a) + m_initloc;
	m_points[m_index + 1] = (Scale(m_size, m_size, m_size) * b) + m_initloc;
	m_points[m_index + 2] = (Scale(m_size, m_size, m_size) * c) + m_initloc;

	vec3 normal = normalize(cross( m_points[m_index + 1] - m_points[m_index] , m_points[m_index + 2] - m_points[m_index + 1]));

	switch (m_shading)
	{
	case FLAT:
		for (int i = m_index; i < m_index + 3; i ++)
		{
			m_normals[i] = normal;
		}
		break;
	case GOURAND:
	case PHONG:
		for (int i = m_index; i < m_index + 3; i ++)
		{
			vec3 pos(m_points[i].x,m_points[i].y,m_points[i].z);
			m_normals[i] = normalize(pos);
		}
		break;
	}

	m_index += 3;
}

point4 CSphere::unit( const point4& p )
{
    float len = p.x * p.x + p.y * p.y + p.z * p.z;
    
    point4 t;
    if ( len > DivideByZeroTolerance )
    {
        t = p / sqrt(len);
        t.w = 1.0;
    }
    return t;
}

void CSphere::divide_triangle( const point4& a, const point4& b, const point4& c, int count )
{
    if ( count > 0 )
    {
        point4 v1 = unit( a + b );
        point4 v2 = unit( a + c );
        point4 v3 = unit( b + c );
        divide_triangle( a, v1, v2, count - 1);
        divide_triangle( c, v2, v3, count - 1);
        divide_triangle( b, v3, v1, count - 1);
        divide_triangle(v1, v3, v2, count - 1);
    }
    else
    {
        triangle(a, b, c );
    }
}

void CSphere::tetrahedron( int count )
{
    divide_triangle(m_tetrahedron_points[0], m_tetrahedron_points[1], m_tetrahedron_points[2], count);
    divide_triangle(m_tetrahedron_points[3], m_tetrahedron_points[2], m_tetrahedron_points[1], count);
    divide_triangle(m_tetrahedron_points[0], m_tetrahedron_points[3], m_tetrahedron_points[1], count);
    divide_triangle(m_tetrahedron_points[0], m_tetrahedron_points[2], m_tetrahedron_points[3], count);
}
